package soulkun.library.custom.view

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import soulkun.library.utils.R
import soulkun.library.utils.logE
import soulkun.library.utils.toPx
import java.util.*
import kotlin.properties.Delegates

/**
 * 自定义流式布局
 *
 */
class FlowLayout : ViewGroup {

    companion object {
        const val TAG = "FlowLayout"
        const val DEFAULT_HORIZONTAL_SPACING = 20  //默认子项的横向间距
        const val DEFAULT_VERTICAL_SPACING = 10  //默认子项的竖向间距
    }

    var isLineLimited by Delegates.notNull<Boolean>()  //是否限制行数
    var maxLines by Delegates.notNull<Int>()  //限制行数时最多行数
    var horizontalSpacing by Delegates.notNull<Int>()  //每个子项的横向间距
    var verticalSpacing by Delegates.notNull<Int>()  //每个子项的竖向间距

    private val allLines: MutableList<List<View>> = ArrayList() // 记录所有的行，一行一行的存储，用于layout
    private var lineHeights: MutableList<Int> = ArrayList() // 记录每一行的行高，用于layout

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initAttrs(context, attrs)
    }

    //初始化属性
    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout)
        isLineLimited = typedArray.getBoolean(R.styleable.FlowLayout_isLineLimited, false)
        maxLines = typedArray.getInteger(R.styleable.FlowLayout_maxLines, 2)
        horizontalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, DEFAULT_HORIZONTAL_SPACING.toPx())
        verticalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, DEFAULT_VERTICAL_SPACING.toPx())
        typedArray.recycle()
    }

    private fun clearMeasureParams() {
        allLines.clear()
        lineHeights.clear()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        clearMeasureParams() //消除内存抖动
        //先度量孩子
        val childCount = childCount
        val paddingLeft = paddingLeft
        val paddingRight = paddingRight
        val paddingTop = paddingTop
        val paddingBottom = paddingBottom
        val selfWidth = MeasureSpec.getSize(widthMeasureSpec) //ViewGroup解析的父亲给我的宽度
        val selfHeight = MeasureSpec.getSize(heightMeasureSpec) // ViewGroup解析的父亲给我的高度
        var lineViews: MutableList<View> = ArrayList() //保存一行中的所有的view
        var lineWidthUsed = 0 //记录这行已经使用了多宽的size
        var lineHeight = 0 // 一行的行高
        var parentNeededWidth = 0 // measure过程中，子View要求的父ViewGroup的宽
        var parentNeededHeight = 0 // measure过程中，子View要求的父ViewGroup的高
        for (i in 0 until childCount) {
            val childView = getChildAt(i)
            val childLP = childView.layoutParams
            if (childView.visibility != GONE) {
                //将layoutParams转变成为measureSpec
                val childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                        childLP.width)
                val childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                        childLP.height)
                childView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
                //获取子view的度量宽高
                val childMeasuredWidth = childView.measuredWidth
                val childMeasuredHeight = childView.measuredHeight
                //如果需要换行
                if (when {
                            lineViews.size >= 1 -> childMeasuredWidth + lineWidthUsed + horizontalSpacing
                            else -> childMeasuredWidth + lineWidthUsed
                        } > selfWidth) {
                    //一旦换行，我们就可以判断当前行需要的宽和高了，所以此时要记录下来
                    allLines.add(lineViews)
                    lineHeights.add(lineHeight)
                    parentNeededHeight += when {
                        allLines.size > 1 -> lineHeight + verticalSpacing
                        else -> lineHeight
                    }
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed)
                    lineViews = ArrayList()
                    lineWidthUsed = 0
                    lineHeight = 0
                    if (isLineLimited) {
                        if (allLines.size >= maxLines) {
                            break
                        }
                    }
                }
                // view 是分行layout的，所以要记录每一行有哪些view，这样可以方便layout布局
                lineViews.add(childView)
                //每行都会有自己的宽和高
                lineWidthUsed += when {
                    lineViews.size > 1 -> childMeasuredWidth + horizontalSpacing
                    else -> childMeasuredWidth
                }
                lineHeight = Math.max(lineHeight, childMeasuredHeight)
                //处理最后一行数据
                if (i == childCount - 1 && allLines.size < maxLines) {
                    allLines.add(lineViews)
                    lineHeights.add(lineHeight)
                    parentNeededHeight += when{
                        allLines.size > 1 -> lineHeight + verticalSpacing
                        else -> lineHeight
                    }
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed)
                }
            }
        }
        //再度量自己,保存
        //根据子View的度量结果，来重新度量自己ViewGroup
        // 作为一个ViewGroup，它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val realWidth = if (widthMode == MeasureSpec.EXACTLY) selfWidth else parentNeededWidth
        val realHeight = if (heightMode == MeasureSpec.EXACTLY) selfHeight else parentNeededHeight
        setMeasuredDimension(realWidth, realHeight)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val lineCount = allLines.size
        var curL = paddingLeft
        var curT = paddingTop
        for (i in 0 until lineCount) {
            val lineViews = allLines[i]
            val lineHeight = lineHeights[i]
            for (j in lineViews.indices) {
                val view = lineViews[j]
                val left = curL
                val top = curT
                val right = left + view.measuredWidth
                val bottom = top + view.measuredHeight
                view.layout(left, top, right, bottom)
                curL = right + horizontalSpacing
            }
            curT += lineHeight + verticalSpacing
            curL = paddingLeft
        }
    }

}